package com.sl.common;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.hash.HashCode;

import static com.google.common.base.Preconditions.checkArgument;

/**
 * Created by lisulong on 2019/5/15.
 */
public class Hashing {
    public static void main(String[] args) {
        int bucket = 5;
        {
            Multimap map = ArrayListMultimap.create();
            for (int i = 0; i < 20; i++) {
                map.put(consistentHash(i, bucket), i);
            }
            System.out.println(bucket + ":" + map);
        }
        System.out.println("-------------");
        bucket = 3;
        {
            Multimap map = ArrayListMultimap.create();
            for (int i = 0; i < 20; i++) {
                map.put(consistentHash(i, bucket), i);
            }
            System.out.println(bucket + ":" + map);
        }
        System.out.println("-------------");
        bucket = 4;
        {
            Multimap map = ArrayListMultimap.create();
            for (int i = 0; i < 20; i++) {
                map.put(consistentHash(i, bucket), i);
            }
            System.out.println(bucket + ":" + map);
        }
    }
    public static int consistentHash(HashCode hashCode, int buckets) {
        return consistentHash(hashCode.padToLong(), buckets);
    }
    public static int consistentHash(long input, int buckets) {
        checkArgument(buckets > 0, "buckets must be positive: %s", buckets);
        LinearCongruentialGenerator generator = new LinearCongruentialGenerator(input);
        int candidate = 0;
        int next;

        // Jump from bucket to bucket until we go out of range
        while (true) {
            next = (int) ((candidate + 1) / generator.nextDouble());
            if (next >= 0 && next < buckets) {
                candidate = next;
            } else {
                return candidate;
            }
        }
    }
    private static final class LinearCongruentialGenerator {
        private long state;

        public LinearCongruentialGenerator(long seed) {
            this.state = seed;
        }

        public double nextDouble() {
            state = 2862933555777941757L * state + 1;
            return ((double) ((int) (state >>> 33) + 1)) / (0x1.0p31);
        }
    }
}
